import { beautify, compiler } from "flowgen";
import { readFile, stat, writeFile } from "fs/promises";
import { glob } from "glob";
import { performance } from "perf_hooks";

const cacheFilename = ".flow_cache";

async function getCache() {
  try {
    return JSON.parse(await readFile(cacheFilename, "utf-8"));
  } catch {
    return {};
  }
}

async function saveCache(cache) {
  await writeFile(cacheFilename, JSON.stringify(cache));
}

async function isCached(cache, sourceFilename, sourceMtime, destFilename) {
  const times = cache[sourceFilename];
  if (!times) return false;
  const [smtime, dmtime] = times;
  if (smtime !== sourceMtime) return false;
  try {
    const dinfo = await stat(destFilename);
    return dinfo.mtimeMs === dmtime;
  } catch {
    return false;
  }
}

async function transpile(cache, sourceFilename) {
  const start = performance.now();
  const destFilename = `${sourceFilename.slice(0, -5)}.js.flow`;
  const sourceInfo = await stat(sourceFilename);

  let verb;
  if (await isCached(cache, sourceFilename, sourceInfo.mtimeMs, destFilename)) {
    verb = "cached";
  } else {
    const contents = await readFile(sourceFilename, "utf-8");
    const flowdef = beautify(compiler.compileDefinitionString(contents));
    const withHeader = `/**
 * Flowtype definitions for ${sourceFilename}
 * Generated by Flowgen from a Typescript Definition
 * @flow
 */
${flowdef}`;
    await writeFile(destFilename, withHeader);
    const destInfo = await stat(destFilename);
    cache[sourceFilename] = [sourceInfo.mtimeMs, destInfo.mtimeMs];
    verb = "transpiled";
  }
  const end = performance.now();
  console.log(`${sourceFilename} ${verb} in ${(end - start).toFixed(1)}ms`);
}

const totalStart = performance.now();
const useCache = process.argv[2] === "--cache";

const [rootCache, files] = await Promise.all([
  useCache ? getCache() : {},
  glob("dist/**/*.d.ts"),
]);
await Promise.all(files.map((file) => transpile(rootCache, file)));
await saveCache(rootCache);
const totalEnd = performance.now();
console.log(
  `finished flow transpiling in ${(totalEnd - totalStart).toFixed(1)}ms`,
);
